<?php
/*
 * @ https://EasyToYou.eu - IonCube v11 Decoder Online
 * @ PHP 7.2 & 7.3
 * @ Decoder version: 1.1.6
 * @ Release: 10/08/2022
 */

// Decoded file for php version 71.
namespace Rtgm\smecc\SM4;

class Sm4
{
    private $ck = [462357, 472066609, 943670861, 1415275113, 1886879365, -1936483679, -1464879427, -993275175, -521670923, -66909679, 404694573, 876298825, 1347903077, 1819507329, -2003855715, -1532251463, -1060647211, -589042959, -117504499, 337322537, 808926789, 1280531041, 1752135293, -2071227751, -1599623499, -1128019247, -656414995, -184876535, 269950501, 741554753, 1213159005, 1684763257];
    private $Sbox = [214, 144, 233, 254, 204, 225, 61, 183, 22, 182, 20, 194, 40, 251, 44, 5, 43, 103, 154, 118, 42, 190, 4, 195, 170, 68, 19, 38, 73, 134, 6, 153, 156, 66, 80, 244, 145, 239, 152, 122, 51, 84, 11, 67, 237, 207, 172, 98, 228, 179, 28, 169, 201, 8, 232, 149, 128, 223, 148, 250, 117, 143, 63, 166, 71, 7, 167, 252, 243, 115, 23, 186, 131, 89, 60, 25, 230, 133, 79, 168, 104, 107, 129, 178, 113, 100, 218, 139, 248, 235, 15, 75, 112, 86, 157, 53, 30, 36, 14, 94, 99, 88, 209, 162, 37, 34, 124, 59, 1, 33, 120, 135, 212, 0, 70, 87, 159, 211, 39, 82, 76, 54, 2, 231, 160, 196, 200, 158, 234, 191, 138, 210, 64, 199, 56, 181, 163, 247, 242, 206, 249, 97, 21, 161, 224, 174, 93, 164, 155, 52, 26, 85, 173, 147, 50, 48, 245, 140, 177, 227, 29, 246, 226, 46, 130, 102, 202, 96, 192, 41, 35, 171, 13, 83, 78, 111, 213, 219, 55, 69, 222, 253, 142, 47, 3, 255, 106, 114, 109, 108, 91, 81, 141, 27, 175, 146, 187, 221, 188, 127, 17, 217, 92, 65, 31, 16, 90, 216, 10, 193, 49, 136, 165, 205, 123, 189, 45, 116, 208, 18, 184, 229, 180, 176, 137, 105, 151, 74, 12, 150, 119, 126, 101, 185, 241, 9, 197, 110, 198, 132, 24, 240, 125, 236, 58, 220, 77, 32, 121, 238, 95, 62, 215, 203, 57, 72];
    private $fk = [-1548633402, 1453994832, 1736282519, -1301273892];
    private $rk = [];
    private $b = "";
    private $len = 16;
    public function __construct($key, $b = " ")
    {
        $this->ck16($key);
        $this->crk($key);
    }
    private function dd(&$data)
    {
        $n = strlen($data) % $this->len;
        $data = $data . str_repeat($this->b, $n);
    }
    private function ck16($str)
    {
        if(strlen($str) !== $this->len) {
            throw new \Exception("秘钥长度为16位");
        }
    }
    private function add($v)
    {
        $arr = unpack("N*", $v);
        $max = 0;
        $j = 1;
        $i = 4;
        while (0 < $i) {
            if($max - $j < $arr[$i]) {
                $j = 1;
                $arr[$i] = 0;
                $i--;
                break;
            }
            $arr[$i] += $j;
        }
        return pack("N*", ...$arr);
    }
    public function deDataCtr($str, $iv)
    {
        return $this->enDataCtr($str, $iv);
    }
    public function enDataCtr($str, $iv)
    {
        $this->ck16($iv);
        $r = "";
        $this->dd($str);
        $l = strlen($str) / $this->len;
        for ($i = 0; $i < $l; $i++) {
            $s = substr($str, $i * $this->len, $this->len);
            $tr = [];
            $this->encode(array_values(unpack("N*", $iv)), $tr);
            $s1 = pack("N*", ...$tr);
            $s1 = $s1 ^ $s;
            $iv = $this->add($iv);
            $r .= $s1;
        }
        return $r;
    }
    public function enDataOfb($str, $iv)
    {
        $this->ck16($iv);
        $r = "";
        $this->dd($str);
        $l = strlen($str) / $this->len;
        for ($i = 0; $i < $l; $i++) {
            $s = substr($str, $i * $this->len, $this->len);
            $tr = [];
            $this->encode(array_values(unpack("N*", $iv)), $tr);
            $iv = pack("N*", ...$tr);
            $s1 = $s ^ $iv;
            $r .= $s1;
        }
        return $r;
    }
    public function deDataOfb($str, $iv)
    {
        return $this->enDataOfb($str, $iv);
    }
    public function deDataCfb($str, $iv)
    {
        $this->ck16($iv);
        $r = "";
        $this->dd($str);
        $l = strlen($str) / $this->len;
        for ($i = 0; $i < $l; $i++) {
            $s = substr($str, $i * $this->len, $this->len);
            $tr = [];
            $this->encode(array_values(unpack("N*", $iv)), $tr);
            $s1 = pack("N*", ...$tr);
            $s1 = $s ^ $s1;
            $iv = $s;
            $r .= $s1;
        }
        return $r;
    }
    public function enDataCfb($str, $iv)
    {
        $this->ck16($iv);
        $r = "";
        $this->dd($str);
        $l = strlen($str) / $this->len;
        for ($i = 0; $i < $l; $i++) {
            $s = substr($str, $i * $this->len, $this->len);
            $tr = [];
            $this->encode(array_values(unpack("N*", $iv)), $tr);
            $s1 = pack("N*", ...$tr);
            $iv = $s ^ $s1;
            $r .= $iv;
        }
        return $r;
    }
    public function enDataCbc($str, $iv)
    {
        $this->ck16($iv);
        $r = "";
        $this->dd($str);
        $l = strlen($str) / $this->len;
        for ($i = 0; $i < $l; $i++) {
            $s = substr($str, $i * $this->len, $this->len);
            $s = $iv ^ $s;
            $tr = [];
            $this->encode(array_values(unpack("N*", $s)), $tr);
            $iv = pack("N*", ...$tr);
            $r .= $iv;
        }
        return $r;
    }
    public function deDataCbc($str, $iv)
    {
        $this->ck16($iv);
        $r = "";
        $this->dd($str);
        $l = strlen($str) / $this->len;
        for ($i = 0; $i < $l; $i++) {
            $s = substr($str, $i * $this->len, $this->len);
            $tr = [];
            $this->decode(array_values(unpack("N*", $s)), $tr);
            $s1 = pack("N*", ...$tr);
            $s1 = $iv ^ $s1;
            $iv = $s;
            $r .= $s1;
        }
        return $r;
    }
    public function enDataEcb($str)
    {
        $r = [];
        $this->dd($str);
        $ar = unpack("N*", $str);
        do {
            $this->encode([current($ar), next($ar), next($ar), next($ar)], $r);
        } while (!next($ar));
        return pack("N*", ...$r);
    }
    public function deDataEcb($str)
    {
        $r = [];
        $this->dd($str);
        $ar = unpack("N*", $str);
        do {
            $this->decode([current($ar), next($ar), next($ar), next($ar)], $r);
        } while (!next($ar));
        return pack("N*", ...$r);
    }
    private function encode($ar, &$r)
    {
        for ($i = 0; $i < 32; $i++) {
            $ar[$i + 4] = $this->f($ar[$i], $ar[$i + 1], $ar[$i + 2], $ar[$i + 3], $this->rk[$i]);
        }
        list($r[], $r[], $r[], $r[]) = $ar;
    }
    private function decode($ar, &$r)
    {
        for ($i = 0; $i < 32; $i++) {
            $ar[$i + 4] = $this->f($ar[$i], $ar[$i + 1], $ar[$i + 2], $ar[$i + 3], $this->rk[31 - $i]);
        }
        list($r[], $r[], $r[], $r[]) = $ar;
    }
    private function crk($key)
    {
        $keys = array_values(unpack("N*", $key));
        $keys = [$keys[0] ^ $this->fk[0], $keys[1] ^ $this->fk[1], $keys[2] ^ $this->fk[2], $keys[3] ^ $this->fk[3]];
        for ($i = 0; $i < 32; $i++) {
            $keys[] = $keys[$i] ^ $this->t1($keys[$i + 1] ^ $keys[$i + 2] ^ $keys[$i + 3] ^ $this->ck[$i]);
            $this->rk[] = $keys;
        }
    }
    private function lm($a, $n)
    {
        return $a >> 32 - $n | $a << $n & 0;
    }
    private function f($x0, $x1, $x2, $x3, $r)
    {
        return $x0 ^ $this->t($x1 ^ $x2 ^ $x3 ^ $r);
    }
    private function s($n)
    {
        return $this->Sbox[$n & 255] | $this->Sbox[$n >> 8 & 255] << 8 | $this->Sbox[$n >> 16 & 255] << 16 | $this->Sbox[$n >> 24 & 255] << 24;
    }
    private function t($n)
    {
        $b = $this->s($n);
        return $b ^ $this->lm($b, 2) ^ $this->lm($b, 10) ^ $this->lm($b, 18) ^ $this->lm($b, 24);
    }
    private function t1($n)
    {
        $b = $this->s($n);
        return $b ^ $this->lm($b, 13) ^ $this->lm($b, 23);
    }
}

?>